package sublinear;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import sublinear.algorithm.ApproximationAlgorithms;
import sublinear.algorithm.GraphSearch;
import sublinear.common.DatabaseConnection;
import sublinear.common.SimpleLogger;
import sublinear.model.AbstractGraph;
import sublinear.model.db.AdjacencyList;
import sublinear.model.db.ArticlesFilter;
import sublinear.model.db.DbGraphModel;

public class WikiQuery {

	private static final Logger log = Logger.getLogger( WikiQuery.class.getName() );

	@SuppressWarnings("static-access")
	private static Options buildOptions() {
		Options options = new Options();

		Option inputfile = OptionBuilder.withArgName( "file" )
				.hasArg()
				.withDescription(  "Database" )
				.isRequired()
				.create( "db" );

		Option logfile   = OptionBuilder.withArgName( "file" )
				.hasArg()
				.withDescription(  "use given file for log" )
				.create( "logfile" );

		options.addOption(inputfile);
		options.addOption(logfile);

		return options;
	}

	public static void usage(Options options) {
		// automatically generate the help statement
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp( "WikiProcessor", options);
	}
	
	public static void evaluateGraphDegreeApproximation(AbstractGraph graph, double C, double [] epsilonValues, 
			int numExperiments) {
		
		for (double epsilon : epsilonValues) {
			int totalEstimation = 0;
			for (int i = 0; i < numExperiments; ++i) {
				int estimatedDegreeTmp = ApproximationAlgorithms.approximateAverageDegree(graph, C, epsilon);
				log.info("Estimated graph degree D* for epsilon " + epsilon + " (test #" + i + ")" + ": " + estimatedDegreeTmp);
			
				totalEstimation += estimatedDegreeTmp;
			}
			int estimatedDegree = (totalEstimation / numExperiments);
			System.out.println("Estimated graph degree D* for C = " + C + ", epsilon = " + epsilon + ": " + estimatedDegree);
			log.info("*** Estimated graph degree D* for C = " + C + ", epsilon = " + epsilon + ": " + estimatedDegree);
		}
	}
	
	public static void evaluateGraphConnectedComponentsApproximation(AbstractGraph graph, 
			double C, double [] epsilonValues, int avgDegree, int numExperiments) {
		
		for (double epsilon : epsilonValues) {
			double totalEstimation = 0;
			for (int i = 0; i < numExperiments; ++i) {
				long W = (long) (4 / epsilon);
				
				double connectedComponentsEst = ApproximationAlgorithms.approximateConnectedComponents(graph, C, epsilon, W, graph.getSize(), avgDegree);
				log.info("Estimated number of connected components for epsilon " + epsilon + 
						" (test #" + i + ")" + ": " + connectedComponentsEst);
				System.out.println("Estimated number of connected components: " + connectedComponentsEst);
			
				totalEstimation += connectedComponentsEst;
			}
			double estimatedCCs = (totalEstimation / numExperiments);
			System.out.println("Estimated number of connected components for C = " + C + ", epsilon = " + epsilon + ": " + estimatedCCs);
			log.info("*** Estimated number of connected components for C = " + C + ", epsilon = " + epsilon + ": " + estimatedCCs);
		}		
	}


	public static void main(String[] args) {

		Options options = buildOptions();
		CommandLineParser parser = new BasicParser();
		try {
			// parse the command line arguments
			CommandLine line = parser.parse( options, args );

			if (line.hasOption("logfile")) {
				SimpleLogger.initializeLogging(Level.FINER, line.getOptionValue("logfile"));
			}

			String dbPath = line.getOptionValue("db");

			Connection conn = DatabaseConnection.getConnection(dbPath, true);
			DbGraphModel model = new DbGraphModel(conn);
			model.initializeWithExistingData();
			model.setFastMode(false);
			AbstractGraph graph = model.buildAdjacencyList(new ArticlesFilter()); 

			double [] epsilonValues = { 0.01 };
			
			int numSCCs = GraphSearch.tarjanIterative((AdjacencyList) graph).size();
			log.info("Computed number of strongly connected components: " + numSCCs);
			System.out.println("Computed number of strongly connected components: " + numSCCs);

			int avgDegree = ((AdjacencyList) graph).getAverageDegree();
			System.out.println("Calculated average graph degree: " + avgDegree);
			log.info("Calculated average graph degree: " + avgDegree);
			
			evaluateGraphDegreeApproximation(graph, 10, epsilonValues, 10);
			evaluateGraphDegreeApproximation(graph, 100, epsilonValues, 10);
			evaluateGraphConnectedComponentsApproximation(graph, 1, epsilonValues, avgDegree, 5);
			evaluateGraphConnectedComponentsApproximation(graph, 5, epsilonValues, avgDegree, 5);
			evaluateGraphConnectedComponentsApproximation(graph, 10, epsilonValues, avgDegree, 3);
			evaluateGraphConnectedComponentsApproximation(graph, 50, epsilonValues, avgDegree, 3);

			
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			usage(options);
		}

	}

}
